//TODO: License Header.

/*
This code is licensed under the BSD license.

*/
package analysis.metrics

import primitives.cluster.ClusterHead
import primitives.graph.Node
import search.util.TreeLoader2
import static Math.min
import primitives.cluster.*;
import primitives.graph.*;

class StateRankMetric /*  extends Metric */{
	
	static Map<Node,Double>  zeroRankings(nodes){
		return rankingsWith(nodes,0);
	}
	
	static Map<Node,Double>  initialRankings(nodes){
		return rankingsWith(nodes,1.0 )//nodes.size())
	}
	
	
	
	static Map<Node,Double>  rankingsWith(nodes, initial_value){
		Map<Node,Double> ranks = [:]
		nodes.each{n->
			ranks[n] = initial_value;
			}
		return ranks;
	}
	
	/**
	 * @return a map of nodes, to the percentage (0 to 1) of the forward links from each node to n.
	*/
	def static backlinks(Node n, nodes){
		def links = [:]
		nodes.each{
			
			node ->
				node.transitionsAsT.each{
					t ->
					def target = t.destinationNode;
					if(target == n){
						links[node] = links.get(node,0) + 1
					}
				}
		}

		
		return links
	}
	
	
	
	def static assignFor(Node n, nodes, oldranks, newranks){
		def multipliers = backlinks(n, nodes);
		def total = 0;
		multipliers.each{
			backlink, amount ->
			def contrib = /* 0.15 + 0.85 **/ (oldranks[backlink] / backlink.transitionsAsN.size())

			total += contrib;
		}
		
		newranks[n] = 0.15 + (0.85 * total);
	}
	
	
	
	def static feedForwardForNode(Node n, oldranks, newranks){
		def rank = oldranks[n];
		
		def ts = n.transitionsAsN;
		
		if(ts.size()  < 1){
			return
		}
		
		
		def contribution  = rank/ts.size();
		
		ts.each{
				target->
				
				newranks[target] = newranks.get(target,0) + contribution;
		}
	}
	
	def static iteration(nodes, oldranks){
		def newranks = zeroRankings(nodes);
		nodes.each{
			//feedForwardForNode(it,oldranks,newranks);
			assignFor(it,nodes,oldranks,newranks)
		}
		return newranks;
	}
	
	def static diff(result1, result2){
		
		def diff = 0.0
		result1.each{
			node,value ->
			diff += Math.abs(value - result2[node])
		}
		return diff
	}
	
	def static getDifferingPairs(result1,result2){
		def res = [:]
		result1.each{
			node,value ->
			if(result2[node] != value){
				res[node] = [lhs:value, rhs:result2[node], diff:value-result2[node]]
			}
		}
		return res
		
	}
	
	
	static def dumpWith(result, text){
		println "digraph g{"
		println "node[colorscheme=pubu9];"
		
		if(text != null){
			println """infoNodeasdf9asdf9a8sd0f9aisdfasdkfjasldfjlasjdfasjdf[label="$text",shape=box]"""
		}
		
		def min=100.0
		def max = 0.0
		result.each{
			_,value->
			if(value > max) max = value
			if(value < min) min = value
		}
		
		result.each{
			node,value->
			def l = node.label
			def range = 0x0..0xff
			
			
			def v = value - min
			def presv = sprintf("%5.3f",value)
			def percent = v/(max - min)
			def amount =((1.0-percent)*(range.size()-1))
			def c = sprintf("#ff%02x%02x",[range[(int)amount],range[(int)amount]])
			println node.label+"""[label="${l}_($presv)",style=filled,fillcolor="$c"];"""
			node.transitionsAsN.each{
				dest ->
				def d = dest.label
				println "$l -> $d;"
			}
		}
		println "}"
		
	}
	
	static def dump(result){
		dumpWith(result,null)
	}

	static def asrteq(val, expected){
		if(val != expected){
			println "Assertion failed, $val != $expected";
		}
	}
	static def evaluate(ClusterHead tree){
		def nodes = tree.nodes;
		
		
		
		def i1 =  initialRankings(nodes)
//		dump(i1)
//		asrteq(sumV(i1),nodes.size())
//		println i1
		def i2 =  iteration(nodes, i1)
//		dump(i2)
//		asrteq(sumV(i2),nodes.size())
//		println i2
//		getDifferingPairs(i1,i2).each{ println it}
		def count = 0;
		while(/* Math.abs(sumV(i2) - nodes.size()) < 0.1 && */ diff(i1,i2) > 0.00000000000002 ){  // && count >= 0){
			if(Math.abs(sumV(i2) - nodes.size()) > 5 ){
				System.err.println("Sanity check failed, difference is > 5")
			}
			i1 = i2;
			i2 = iteration(nodes,i1);
			count++
		}
		
		return [i2,diff(i1,i2),count]
	}
	
	static def sumV(res){
		def s = 0.0
		res.each{n,v->s+= v}
		return s
	}
	
	@Deprecated
	public static void main(String[] args){
		def filename = args[0]
		
		ClusterHead tree = null;
		
		if(filename.endsWith("ClusterHead")){
			tree = TreeLoader2.loadTreeFromSerial(new File(filename))
		}else if(filename.endsWith("dot")){
			tree = TreeLoader2.loadTreeFromDot(new File(filename))
		}
		def (result,totaldiff, count) = evaluate(tree)
		
		
		def sum = sumV(result)
		def size = tree.nodes.size();
		sum = sprintf("%5.5f",sum)
		dumpWith(result,"Total diff was $totaldiff, yielded after $count iterations.  Sum of values is $sum (expected sum is $size)")
		
		
	}
	
}